package org.osito.jbehave.junit;

import java.util.ArrayList;
import java.util.List;

import org.jbehave.core.failures.FailingUponPendingStep;
import org.jbehave.core.failures.PendingStepFound;
import org.jbehave.core.failures.PendingStepStrategy;
import org.jbehave.core.model.Story;
import org.jbehave.core.reporters.NullStoryReporter;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;

class JUnitStoryReporter extends NullStoryReporter {

	private final List<Description> scenarios = new ArrayList<Description>();
	private final RunNotifier notifier;
	private Description currentScenario;
	private Story givenStory;

	private Throwable cause;
	private boolean failed;
	private boolean ignored;
	private final PendingStepStrategy pendingStepStrategy;

	JUnitStoryReporter(RunNotifier notifier, Description storyDesc, PendingStepStrategy pendingStepStrategy) {
		this.notifier = notifier;
		this.pendingStepStrategy = pendingStepStrategy;
		scenarios.addAll(storyDesc.getChildren());
	}

	@Override
	public void beforeStory(Story story, boolean givenStory) {
		if (givenStory) {
			this.givenStory = story;
		}
	}

	@Override
	public void afterStory(boolean givenStory) {
		if (givenStory) {
			this.givenStory = null;
		}
	}

	private boolean isNotGivenStory() {
		return givenStory == null;
	}

	@Override
	public void beforeScenario(String title) {
		if (isNotGivenStory()) {
			title = title.replaceAll("\n", " ");
			for (Description scenario : scenarios) {
				if (scenario.getDisplayName().startsWith("Scenario: " + title)) {
					currentScenario = scenario;
					break;
				}
			}
			notifier.fireTestStarted(currentScenario);
		}
	}

	@Override
	public void afterScenario() {
		if (isNotGivenStory()) {

			if (failed) {
				notifier.fireTestFailure(new Failure(currentScenario, cause));
			} else if (ignored) {
				notifier.fireTestIgnored(currentScenario);
			} else {
				notifier.fireTestFinished(currentScenario);
			}
			currentScenario = null;
			failed = false;
			cause = null;
			ignored = false;
		}
	}

	@Override
	public void pending(String step) {
		if (isNotGivenStory()) {

			if (pendingStepStrategy instanceof FailingUponPendingStep) {
				failed = true;
				cause = new PendingStepFound(step);
			} else {
				ignored = true;
			}
		}
	}

	@Override
	public void failed(String step, Throwable cause) {
		if (isNotGivenStory()) {
			failed = true;
			this.cause = cause;
		}
	}

}
